﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net.Sockets;

namespace Sofire.DataComm.Net.Async
{
    /// <summary>
    /// 一个可靠的异步套接字的客户端实现。
    /// </summary>
    public class AsyncSocketClient : AsyncSocketBase, IRemote
    {
        private static int UnknownNameIndex = 0;

        private SocketAsyncEventArgsClient ServerReceiveArgs;
        private SocketAsyncEventArgsClient ServerSendArgs;

        private TimeSpan _ConnectTimeout = new TimeSpan(0, 0, 5);
        /// <summary>
        /// 设置或获取一个值，表示连接到远程服务端的超时时间。
        /// </summary>
        public TimeSpan ConnectTimeout
        {
            get
            {
                return this._ConnectTimeout;
            }
            set
            {
                this._ConnectTimeout = value;
            }
        }

        private int _MaxReceiveBufferSize2 = 2048 + DefaultMessageSize;

        private int _MaxReceiveBufferSize = 2048;
        /// <summary>
        /// 设置或获取接收的最大缓冲区长度。默认为 2048 字节。
        /// </summary>
        public int MaxReceiveBufferSize
        {
            get
            {
                return this._MaxReceiveBufferSize;
            }
            set
            {
                if(value < 128) value = 128;
                this._MaxReceiveBufferSize = value;
                this._MaxReceiveBufferSize2 = value + 5;
            }
        }

        private Socket _Server;
        /// <summary>
        /// 获取服务端的 <see cref="System.Net.Sockets"/>。
        /// </summary>
        public Socket Server
        {
            get
            {
                return this._Server;
            }
        }

        /// <summary>
        /// 获取一个值，指示当前异步套接字是否处于忙碌状态。
        /// </summary>
        public override bool IsBusy
        {
            get
            {
                return (this.ServerReceiveArgs == null || this.ServerReceiveArgs.IsBusy) && base.IsBusy;
            }
        }

        /// <summary>
        /// 指定基础信息，创建一个 TCP 客户端。
        /// </summary>
        /// <param name="name">异步套接字的唯一标识符。</param>
        /// <param name="ip">网络 IP。</param>
        /// <param name="port">网络端口。</param>
        /// <returns>返回一个新的 TCP 客户端。</returns>
        public static AsyncSocketClient CreateTcp(string name, string ip, int port)
        {
            return new AsyncSocketClient(name, new SocketInfo(ProtocolMode.Tcp, ip, port));
        }

        /// <summary>
        /// 指定基础信息，创建一个 TCP 客户端。
        /// </summary>
        /// <param name="ip">网络 IP。</param>
        /// <param name="port">网络端口。</param>
        /// <returns>返回一个新的 TCP 客户端。</returns>
        public static AsyncSocketClient CreateTcp(string ip, int port)
        {
            return CreateTcp(null, ip, port);
        }

        /// <summary>
        /// 指定基础信息，创建一个 UDP 客户端。
        /// </summary>
        /// <param name="name">异步套接字的唯一标识符。</param>
        /// <param name="ip">网络 IP。</param>
        /// <param name="port">网络端口。</param>
        /// <returns>返回一个新的 UDP 客户端。</returns>
        public static AsyncSocketClient CreateUdp(string name, string ip, int port)
        {
            return new AsyncSocketClient(name, new SocketInfo(ProtocolMode.Udp, ip, port));
        }

        /// <summary>
        /// 指定基础信息，创建一个 UDP 客户端。
        /// </summary>
        /// <param name="ip">网络 IP。</param>
        /// <param name="port">网络端口。</param>
        /// <returns>返回一个新的 UDP 客户端。</returns>
        public static AsyncSocketClient CreateUdp(string ip, int port)
        {
            return CreateUdp(null, ip, port);
        }

        /// <summary>
        /// 初始化 <see cref="Sofire.DataComm.Net.Async.AsyncSocketClient"/> 的新实例。
        /// </summary>
        /// <param name="name">异步套接字的唯一标识符。</param>
        /// <param name="socketInfo">套接字的信息。</param>
        public AsyncSocketClient(string name, SocketInfo socketInfo)
            : base(string.IsNullOrEmpty(name) ? "AsyncSocketClient" + Interlocked.Increment(ref UnknownNameIndex) : name, socketInfo) { }

        /// <summary>
        /// 打开通讯时发生。
        /// </summary>
        protected override void OnOpen()
        {
            ManualResetEvent ConnectTimeoutObject = new ManualResetEvent(false);
            Exception connectEx = null;
            bool isConnected = false;
            bool isTimeout = false;
            var socketInfo = this.SocketInfo;
            this._Server = socketInfo.CreateSocket();

            this._Server.SendBufferSize = this._MaxReceiveBufferSize2;
            this._Server.ReceiveBufferSize = this._MaxReceiveBufferSize2;
            var connectAsync = new SocketAsyncEventArgs();
            connectAsync.RemoteEndPoint = socketInfo.EndPoint;


            EventHandler<SocketAsyncEventArgs> actionT = (object ss, SocketAsyncEventArgs ee) =>
            {
                if(ee.SocketError == SocketError.Success)
                {
                    try
                    {
                        //在超时以后进行连接的
                        if(!isTimeout)
                        {
                            var e = new SocketAsyncEventArgsClient(true, false);
                            e.AcceptSocket = this._Server;

                            e.BufferHandler = new ReceiveTranslater(this);
                            e.Completed += this.SocketAsyncCompleted;
                            e.SetBuffer(new byte[this._MaxReceiveBufferSize2], 0, this._MaxReceiveBufferSize2);

                            this.ServerReceiveArgs = e;
                            this.ServerSendArgs = new SocketAsyncEventArgsClient(false, true);
                            this.ServerSendArgs.AcceptSocket = this._Server;
                            this.ServerSendArgs.Completed += this.SocketAsyncCompleted;
                            this.ServerSendArgs.SetBuffer(new byte[this._MaxReceiveBufferSize2], 0, this._MaxReceiveBufferSize2);
                            this.ServerSendArgs.InitSendMode();

                            this._Guid = Guid.NewGuid();
                            this._AcceptTime = DateTime.Now;

                            if(!this._Server.ReceiveAsync(e))
                            {
                                this.ProcessReceive(e);
                            }
                        }
                    }
                    catch(Exception ex)
                    {
                        connectEx = ex;
                    }
                }
                else
                {
#if SILVERLIGHT
                    
                    connectEx = ee.ConnectByNameError;
#else
                    connectEx = new SocketException((int)ee.SocketError);
#endif

                    //http://silverlightchina.net/html/tips/2011/0314/6066_2.html
                    //您可能需要与服务的所有者联系，以公开通过 HTTP 的套接字跨域策略，并在允许的套接字端口范围 4502-4534 之内承载该服务。
                    //试图以其访问权限所禁止的方式访问套接字。
                    //msdn.microsoft.com/zh-cn/library/cc645032(v=vs.95).aspx
                }
                isConnected = true;
                ConnectTimeoutObject.Set();
            };


            connectAsync.Completed += actionT;
            if(!this._Server.ConnectAsync(connectAsync))
            {
                actionT(this, connectAsync);
            }


            ConnectTimeoutObject.WaitOne(this._ConnectTimeout
#if !SILVERLIGHT
, true
#endif
);

            if(isConnected)
            {
                if(connectEx != null) throw connectEx;
                return;
            }
            isTimeout = true;
            throw new TimeoutException();
        }

        /// <summary>
        /// 关闭通讯时发生。
        /// </summary>
        protected override void OnClose()
        {
            if(this._Server == null) return;
            if(this.ServerReceiveArgs != null)
            {
                while(this.IsBusy)
                {
                    Thread.Sleep(333);
                }
            }
            if(this._Server.Connected)
            {
                try
                {
                    this._Server.Shutdown(SocketShutdown.Both);
                }
                catch(Exception) { }
                this._Server.Close();
            }
            if(this.ServerSendArgs != null) this.ServerSendArgs.Set();
            this._Server = null;
        }

        /// <summary>
        /// 异步套接字抛出异常后发生的方法。该方法会直接断开与服务端的连接。
        /// </summary>
        /// <param name="asyncEventArgs">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="operation">异步套接字操作的类型。</param>
        /// <param name="exception">抛出的异常信息。</param>
        protected override void RaiseSocketFaild(SocketAsyncEventArgs asyncEventArgs, SocketOperation operation, Exception exception)
        {
            base.RaiseSocketFaild(asyncEventArgs, operation, exception);
            this.Close();
        }

        /// <summary>
        /// 异步套接字发送数据后发生。
        /// </summary>
        /// <param name="e">当前事件参数关联的 <see cref="System.Net.Sockets.SocketAsyncEventArgs"/>。</param>
        /// <param name="sendLength">已发送的数据长度。</param>
        protected override void RaiseSend(SocketAsyncEventArgs e, int sendLength)
        {
            base.RaiseSend(e, sendLength);
        }

        #region Full Send Mode

        #region >> Predefine Mode <<

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送一个  <see cref="System.Int32"/>  和 <see cref="System.String"/> 到服务端。
        /// </summary>
        /// <param name="intValue">发送的整形。</param>
        /// <param name="stringValue">发送的字符串。</param>
        public void Send(int intValue, string stringValue)
        {
            this.Send(intValue, stringValue, null);
        }

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送一个  <see cref="System.Int32"/>  和 <see cref="System.String"/> 到服务端。
        /// </summary>
        /// <param name="intValue">发送的整形。</param>
        /// <param name="stringValue">发送的字符串。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void Send(int intValue, string stringValue, object userToken)
        {
            var stringBytes = this.DefaultEncoding.GetBytes(stringValue);
            var stringByteLength = stringBytes.Length;
            byte[] buffer = new byte[stringByteLength + 4];
            int offset = 0;
            BufferConvert.WriteInt32(intValue, buffer, ref offset);
            Buffer.BlockCopy(stringBytes, 0, buffer, offset, stringByteLength);

            this.Send(ReceiveMode.Full, buffer, 0, buffer.Length, userToken);
        }

        /// <summary>
        /// 使用默认的编码格式和 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送 <see cref="System.String"/> 到服务端。
        /// </summary>
        /// <param name="value">发送的字符串。</param>
        public void Send(string value)
        {
            this.Send(value, null);
        }

        /// <summary>
        /// 使用默认的编码格式和 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送 <see cref="System.String"/> 到服务端。
        /// </summary>
        /// <param name="value">发送的字符串。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void Send(string value, object userToken)
        {
            var buffer = this.DefaultEncoding.GetBytes(value);
            this.Send(ReceiveMode.Full, buffer, 0, buffer.Length, userToken);
        }

        #endregion

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        public void Send(byte[] buffer)
        {
            this.Send(ReceiveMode.Full, buffer, 0, buffer.Length, null);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        public void Send(byte[] buffer, int offset, int count)
        {
            this.Send(ReceiveMode.Full, buffer, offset, count, null);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void Send(byte[] buffer, object userToken)
        {
            this.Send(ReceiveMode.Full, buffer, 0, buffer.Length, userToken);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Full"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void Send(byte[] buffer, int offset, int count, object userToken)
        {
            this.Send(ReceiveMode.Full, buffer, offset, count, userToken);
        }

        #endregion

        #region Once Send Mode

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Once"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        public void OnceSend(byte[] buffer)
        {
            this.Send(ReceiveMode.Once, buffer, 0, buffer.Length, null);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Once"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void OnceSend(byte[] buffer, object userToken)
        {
            this.Send(ReceiveMode.Once, buffer, 0, buffer.Length, null);
        }

        /// <summary>
        /// 使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Once"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        public void OnceSend(byte[] buffer, int offset, int count)
        {
            this.Send(ReceiveMode.Once, buffer, offset, count, null);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.Once"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void OnceSend(byte[] buffer, int offset, int count, object userToken)
        {
            this.Send(ReceiveMode.Once, buffer, offset, count, userToken);
        }

        #endregion

        #region None Send Mode

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.None"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        public void NoneSend(byte[] buffer)
        {
            this.Send(ReceiveMode.None, buffer, 0, buffer.Length, null);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.None"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void NoneSend(byte[] buffer, object userToken)
        {
            this.Send(ReceiveMode.None, buffer, 0, buffer.Length, null);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.None"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        public void NoneSend(byte[] buffer, int offset, int count)
        {
            this.Send(ReceiveMode.None, buffer, offset, count, null);
        }

        /// <summary>
        /// 指定与发送操作关联的用户自定义数据，使用 <see cref="Sofire.DataComm.Net.Async.ReceiveMode.None"/> 发送数据到服务端。
        /// </summary>
        /// <param name="buffer">数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        public void NoneSend(byte[] buffer, int offset, int count, object userToken)
        {
            this.Send(ReceiveMode.None, buffer, offset, count, userToken);
        }

        #endregion

        /// <summary>
        /// 指定接收模式，发送数据到服务端。
        /// </summary>
        /// <param name="mode">数据接收的模式。</param>
        /// <param name="buffer">要用于异步套接字方法的数据缓冲区。</param>
        /// <param name="offset">数据缓冲区中操作开始位置处的偏移量，以字节为单位。</param>
        /// <param name="count">可在缓冲区中发送或接收的最大数据量（以字节为单位）。</param>
        /// <param name="userToken">与发送操作关联的用户自定义数据。</param>
        protected void Send(ReceiveMode mode, byte[] buffer, int offset, int count, object userToken)
        {
            this.SendTo(this.ServerSendArgs, mode, buffer, offset, count, userToken);
        }

        #region IRemoteClient 成员

        private Guid _Guid;
        Guid IRemote.Guid
        {
            get { return this._Guid; }
        }

        private DateTime _AcceptTime;
        DateTime IRemote.AcceptTime
        {
            get { return this._AcceptTime; }
        }

        private object _Tag;
        object IRemote.UserToken
        {
            get
            {
                return this._Tag;
            }
            set
            {
                this._Tag = value;
            }
        }

        void IRemote.Shutdown()
        {
            this.Close();
        }

        #endregion
    }
}
